

#include <assert.h>
#include <gfx/iprogram.h>
#include <gfx/iprogramparameter.h>
#include <gfx/gl20/vertexdeclaration_gl13.h>
#include <map>
#include <GL/glew.h>
#include <stdio.h>

#define VOIDPTR(base, offset) (void*)(((long)base) + offset)

namespace
{


const ceVertexElement* find_element (const ceVertexElement* elems, unsigned stream, unsigned idx)
{
  unsigned v=0;
	for (const ceVertexElement* ve = elems; ve->Name.IsValid(); ve++)
    {
      if (ve->Stream != stream)
        {
          continue;
        }
      if (v == idx)
        {
          return ve;
        }
      v++;
    }
  return 0;
}


void copy_element (ceVertexElement* dst, const ceVertexElement* src)
{
  assert (dst);
  if (src)
    {
			dst->Name = src->Name;
			dst->Type = src->Type;
      dst->Offset = src->Offset;
      dst->Size = src->Size;
      dst->Stream = src->Stream;
      dst->Stride = src->Stride;
    }
}
}

ceVertexDeclaration::ceVertexDeclaration(const ceVertexElement* elements)
  : _streams(0)
  , _elements (0)
  , _totalSize (0)
{
  CE_OBJECT_CONSTR

  // evaluate the number of streams and the number of elements per stream
  std::map<unsigned, unsigned> str;
  std::map<unsigned, unsigned> strSize;
	for (const ceVertexElement* vptr=elements; vptr->Name.IsValid(); vptr++)
    {
      (str[vptr->Stream])++;
      _streams = _streams < vptr->Stream ? vptr->Stream : _streams;
      strSize[vptr->Stream] = vptr->Stride;
    }
  _streams++;



  // now create elements grouped by the streams.
  _elements = new ceVertexElement*[_streams];
  for (unsigned i=0; i<_streams; i++)
    {
      unsigned numElems = str[i]+1;
      // create the elements for this stream
      _elements[i] = new ceVertexElement[numElems];

      for (unsigned j=0; j<numElems; j++)
        {
          copy_element(&_elements[i][j], find_element(elements, i, j));
        }
      _totalSize += strSize[i];
    }
}

ceVertexDeclaration::~ceVertexDeclaration()
{
  for (unsigned i=0; i<_streams; i++)
    {
      delete [] _elements[i];
    }
  delete [] _elements;
}


unsigned ceVertexDeclaration::GetNumberOfStreams() const
{
  return _streams;
}

const ceVertexElement* ceVertexDeclaration::GetElements(unsigned stream) const
{
  assert (stream < _streams);

  return _elements[stream];
}

const ceVertexElement* ceVertexDeclaration::GetElement(ceProgramParameterName name, unsigned stream) const
{
  const ceVertexElement* elements = GetElements(stream);
  if (!elements)
    {
      return 0;
    }
	while (elements && elements->Name.IsValid ())
    {
      if (elements->Name == name)
        {
          return elements;
        }
      elements++;
    }
  return 0;
}

void ceVertexDeclaration::BindStream(iProgram* program, unsigned stream, void* ptr)
{
  assert (stream < _streams);

  ceVertexElement* elements = _elements[stream];

  if (program)
    {
			while (elements && elements->Name.IsValid ())
        {
          iProgramParameter* p = program->GetParameter(elements->Name);
          if (p)
						{
              p->Bind(elements->Size, elements->Stride, VOIDPTR(ptr, elements->Offset));
              p->Enable();
            }
          elements++;
        }
    }
}

void ceVertexDeclaration::UnbindStream(iProgram* program, unsigned stream)
{
  assert (stream < _streams);

  ceVertexElement* elements = _elements[stream];

  if (program)
    {
			while (elements && elements->Name.IsValid ())
        {
          iProgramParameter* p = program->GetParameter(elements->Name);
          if (p)
						{
              p->Disable();
            }
          elements++;
        }
    }
}

unsigned ceVertexDeclaration::GetTotalSize () const
{
  return _totalSize;
}


